import bcrypt from 'bcryptjs';
import crypto from 'crypto';
import { prisma } from './prisma';

// 生成唯一slug
export const generateSlug = async (
  text: string, 
  model: 'knowledgeBase' | 'category' | 'tag',
  excludeId?: string
): Promise<string> => {
  // 转换为小写并替换特殊字符
  let slug = text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '') // 移除特殊字符
    .replace(/[\s_-]+/g, '-') // 将空格、下划线替换为连字符
    .replace(/^-+|-+$/g, ''); // 移除首尾连字符

  // 如果slug为空，使用时间戳
  if (!slug) {
    slug = `item-${Date.now()}`;
  }

  let finalSlug = slug;
  let counter = 1;

  // 检查slug是否已存在
  while (true) {
    let exists = false;

    switch (model) {
      case 'knowledgeBase':
        exists = await prisma.knowledgeBase.findUnique({
          where: { slug: finalSlug }
        }) !== null;
        break;
      case 'category':
        exists = await prisma.category.findUnique({
          where: { slug: finalSlug }
        }) !== null;
        break;
      case 'tag':
        exists = await prisma.tag.findUnique({
          where: { slug: finalSlug }
        }) !== null;
        break;
    }

    // 如果存在且不是当前记录，添加计数器
    if (exists && (!excludeId || finalSlug !== slug)) {
      finalSlug = `${slug}-${counter}`;
      counter++;
    } else {
      break;
    }
  }

  return finalSlug;
};

// 密码哈希
export const hashPassword = async (password: string): Promise<string> => {
  const saltRounds = 12;
  return bcrypt.hash(password, saltRounds);
};

// 密码验证
export const verifyPassword = async (password: string, hashedPassword: string): Promise<boolean> => {
  return bcrypt.compare(password, hashedPassword);
};

// 生成随机字符串
export const generateRandomString = (length: number = 32): string => {
  return crypto.randomBytes(length).toString('hex');
};

// 生成随机数字
export const generateRandomNumber = (min: number, max: number): number => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

// 生成JWT令牌
export const generateJWT = (payload: any, secret: string, expiresIn: string = '24h'): string => {
  const header = {
    alg: 'HS256',
    typ: 'JWT'
  };

  const now = Math.floor(Date.now() / 1000);
  const exp = now + (expiresIn.includes('h') ? parseInt(expiresIn) * 3600 : parseInt(expiresIn));

  const jwtPayload = {
    ...payload,
    iat: now,
    exp
  };

  const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
  const encodedPayload = Buffer.from(JSON.stringify(jwtPayload)).toString('base64url');

  const signature = crypto
    .createHmac('sha256', secret)
    .update(`${encodedHeader}.${encodedPayload}`)
    .digest('base64url');

  return `${encodedHeader}.${encodedPayload}.${signature}`;
};

// 验证JWT令牌
export const verifyJWT = (token: string, secret: string): any => {
  try {
    const [encodedHeader, encodedPayload, signature] = token.split('.');

    // 验证签名
    const expectedSignature = crypto
      .createHmac('sha256', secret)
      .update(`${encodedHeader}.${encodedPayload}`)
      .digest('base64url');

    if (signature !== expectedSignature) {
      throw new Error('Invalid signature');
    }

    // 解析payload
    const payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString());

    // 检查过期时间
    if (payload.exp && Date.now() >= payload.exp * 1000) {
      throw new Error('Token expired');
    }

    return payload;
  } catch (error) {
    throw new Error('Invalid token');
  }
};

// 格式化文件大小
export const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return '0 Bytes';

  const k = 1024;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));

  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};

// 格式化日期
export const formatDate = (date: Date, format: string = 'YYYY-MM-DD HH:mm:ss'): string => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');

  return format
    .replace('YYYY', String(year))
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hours)
    .replace('mm', minutes)
    .replace('ss', seconds);
};

// 计算相对时间
export const getRelativeTime = (date: Date): string => {
  const now = new Date();
  const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

  if (diffInSeconds < 60) {
    return '刚刚';
  } else if (diffInSeconds < 3600) {
    return `${Math.floor(diffInSeconds / 60)}分钟前`;
  } else if (diffInSeconds < 86400) {
    return `${Math.floor(diffInSeconds / 3600)}小时前`;
  } else if (diffInSeconds < 2592000) {
    return `${Math.floor(diffInSeconds / 86400)}天前`;
  } else if (diffInSeconds < 31536000) {
    return `${Math.floor(diffInSeconds / 2592000)}个月前`;
  } else {
    return `${Math.floor(diffInSeconds / 31536000)}年前`;
  }
};

// 截取文本
export const truncateText = (text: string, maxLength: number, suffix: string = '...'): string => {
  if (text.length <= maxLength) {
    return text;
  }
  return text.substring(0, maxLength - suffix.length) + suffix;
};

// 生成分页信息
export const generatePagination = (page: number, limit: number, total: number) => {
  const totalPages = Math.ceil(total / limit);
  const hasNextPage = page < totalPages;
  const hasPrevPage = page > 1;

  return {
    page,
    limit,
    total,
    totalPages,
    hasNextPage,
    hasPrevPage,
    nextPage: hasNextPage ? page + 1 : null,
    prevPage: hasPrevPage ? page - 1 : null
  };
};

// 构建搜索查询
export const buildSearchQuery = (searchTerm: string, fields: string[]) => {
  if (!searchTerm) return {};

  const searchConditions = fields.map(field => ({
    [field]: {
      contains: searchTerm,
      mode: 'insensitive' as const
    }
  }));

  return {
    OR: searchConditions
  };
};

// 构建排序条件
export const buildSortQuery = (sortBy: string, sortOrder: string = 'desc') => {
  const validSortFields = ['createdAt', 'updatedAt', 'title', 'name', 'viewCount', 'likeCount'];
  const validSortOrders = ['asc', 'desc'];

  if (!validSortFields.includes(sortBy)) {
    sortBy = 'createdAt';
  }

  if (!validSortOrders.includes(sortOrder)) {
    sortOrder = 'desc';
  }

  return { [sortBy]: sortOrder };
};

// 验证邮箱格式
export const isValidEmail = (email: string): boolean => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
};

// 验证URL格式
export const isValidUrl = (url: string): boolean => {
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
};

// 生成安全的文件名
export const generateSafeFileName = (originalName: string): string => {
  const timestamp = Date.now();
  const randomString = generateRandomString(8);
  const extension = originalName.split('.').pop() || '';
  
  return `${timestamp}-${randomString}.${extension}`;
};

// 清理HTML标签
export const stripHtmlTags = (html: string): string => {
  return html.replace(/<[^>]*>/g, '');
};

// 生成摘要
export const generateExcerpt = (content: string, maxLength: number = 200): string => {
  const plainText = stripHtmlTags(content);
  return truncateText(plainText, maxLength);
};

// 计算阅读时间（基于中文和英文单词数）
export const calculateReadingTime = (content: string): number => {
  const chineseWords = (content.match(/[\u4e00-\u9fa5]/g) || []).length;
  const englishWords = (content.match(/[a-zA-Z]+/g) || []).length;
  const totalWords = chineseWords + englishWords;
  
  // 假设平均阅读速度为每分钟200个单词
  return Math.ceil(totalWords / 200);
};

// 生成唯一ID
export const generateUniqueId = (): string => {
  return crypto.randomUUID();
};

// 深度克隆对象
export const deepClone = <T>(obj: T): T => {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  if (obj instanceof Date) {
    return new Date(obj.getTime()) as unknown as T;
  }

  if (obj instanceof Array) {
    return obj.map(item => deepClone(item)) as unknown as T;
  }

  if (typeof obj === 'object') {
    const clonedObj = {} as T;
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        clonedObj[key] = deepClone(obj[key]);
      }
    }
    return clonedObj;
  }

  return obj;
};
